﻿using System;
using System.Diagnostics;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.Serialization;
using System.Xml.Serialization;

namespace Nature.Data
{
    /// <summary>
    /// Related values of temperature T, isobatic heat capacity Cp, enthalpy H and entropy S
    /// </summary>
    [DataContract]
    [Serializable]
    public class ThermoRef : IEquatable<ThermoRef>, IRoughlyEquatable<ThermoRef>
    {
        #region Private Fields
        double? m_t, m_cp, m_h, m_s; 
        #endregion

        #region Constructors
        /// <summary>
        /// Initializes a new instance of the ThermoRef data type
        /// </summary>
        public ThermoRef() { }

        /// <summary>
        /// Initializes a new instance of the ThermoRef data type
        /// </summary>
        /// <param name="t">Temperature [K]</param>
        /// <param name="cp">Isobaric heat capacity [J/mol/K]</param>
        /// <param name="h">Enthalpy [J/mol]</param>
        /// <param name="s">Entropy [J/mol/K]</param>
        public ThermoRef(double t, double cp, double h, double s)
        {
            this.T = t;
            this.Cp = cp;
            this.H = h;
            this.S = s;
        }

        /// <summary>
        /// Initializes a new instance of the ThermoRef data type by copying the data from the given object of the same type
        /// </summary>
        /// <param name="thermo7">Source object</param>
        public ThermoRef(Thermo7Range thermo7)
        {
            this.T = thermo7.Tmin;
            this.ReducedCp = thermo7.Cpmin;
            this.ReducedH = thermo7.Hmin;
            this.ReducedS = thermo7.Smin;
        } 
        #endregion

        #region Properties

        /// <summary>
        /// Gets an indication whether this instance is initialized and therefore can be used in computations
        /// </summary>
        public bool Undefined
        {
            get { return m_t == null || m_cp == null || m_h == null || m_s == null; }
        }

        /// <summary>
        /// Reference temperature [K]
        /// </summary>
        [DataMember]
        public double T
        {
            get { return m_t.Value; }
            set { m_t = value; }
        }

        /// <summary>
        /// Gets or sets the molar isobaric heat capacity [J/mol/K]
        /// </summary>
        [DataMember]
        public double Cp
        {
            get { return m_cp.Value; }
            set { m_cp = value; }
        }

        /// <summary>
        /// Gets or sets the molar enthalpy [J/mol]
        /// </summary>
        [DataMember]
        public double H
        {
            get { return m_h.Value; }
            set { m_h = value; }
        }

        /// <summary>
        /// Gets or sets the molar entropy [J/mol/K]
        /// </summary>
        [DataMember]
        public double S
        {
            get { return m_s.Value; }
            set { m_s = value; }
        }

        /// <summary>
        /// Gets or sets the molar isochoric heat capacity [J/mol/K]
        /// </summary>
        [XmlIgnore]
        public double Cv
        {
            get { return (Cp - Constants.Rgas); }
            set { Cp = (value + Constants.Rgas); }
        }

        /// <summary>
        /// Gets or sets the molar internal energy  [J/mol]
        /// </summary>
        [XmlIgnore]
        public double U
        {
            get { return (H - T * Constants.Rgas); }
            set { H = (value + T * Constants.Rgas); }
        }

        /// <summary>
        /// Gets or sets the reduced isochoric heat capacity [~]
        /// </summary>
        [XmlIgnore]
        public double ReducedCp
        {
            get { return m_cp.Value / Constants.Rgas; }
            set { m_cp = value * Constants.Rgas; }
        }

        /// <summary>
        /// Gets or sets the reduced enthalpy [~]
        /// </summary>
        [XmlIgnore]
        public double ReducedH
        {
            get { return m_h.Value / Constants.Rgas / m_t.Value; }
            set { m_h = value * Constants.Rgas * m_t.Value; }
        }

        /// <summary>
        /// Gets or sets the reduced entropy [~]
        /// </summary>
        [XmlIgnore]
        public double ReducedS
        {
            get { return m_s.Value / Constants.Rgas; }
            set { m_s = value * Constants.Rgas; }
        }

        #endregion

        #region Object Methods

        /// <summary>
        /// Converts the value of this instance to a System.String.
        /// </summary>
        /// <returns>A string representation of this instance</returns>
        public override string ToString()
        {
            if (Undefined) { return "Undefined"; }
            string delimiter = ",   ";
            StringBuilder sb = new StringBuilder();
            sb.AppendFormat("T: {0}[{1}]", m_t, TemperatureUnits.Kelvin);
            sb.Append(delimiter);
            sb.AppendFormat("Cp: {0} [~]", ReducedCp);
            sb.Append(delimiter);
            sb.AppendFormat("H: {0}[~]", ReducedH);
            sb.Append(delimiter);
            sb.AppendFormat("S: {0}[~]", ReducedS);
            return sb.ToString();
        }

        /// <summary>
        /// Determines whether the specified System.Object is equal to the current System.Object.
        /// </summary>
        /// <param name="obj">The System.Object to compare with the current System.Object.</param>
        /// <returns>true if the specified System.Object is equal to the current System.Object; otherwise, false.</returns>
        public override bool Equals(object obj)
        {
            ThermoRef other = obj as ThermoRef;
            if (false == Object.ReferenceEquals(other, null)) 
            { 
                return this.Equals(other); 
            }
            return base.Equals(obj);
        }

        /// <summary>
        /// Returns the hash code for this instance.
        /// </summary>
        /// <returns>A 32-bit signed integer hash code.</returns>
        public override int GetHashCode()
        {
            if ( Undefined ) { return (0).GetHashCode(); }
            return
                T.GetHashCode() / 4
                + Cp.GetHashCode() / 4
                + H.GetHashCode() / 4
                + S.GetHashCode() / 4;

        }
        #endregion

        #region IEquatable<ThermoRef> Members

        /// <summary>
        /// Returns a value indicating whether this instance and a specified ThermoRef object represent the same value.
        /// </summary>
        /// <param name="other">The ThermoRef to compare with the current ThermoRef</param>
        /// <returns>true if the specified ThermoRef object is equal to this instance; otherwise, false.</returns>
        public bool Equals(ThermoRef other)
        {
            if (Object.ReferenceEquals(other, null)) { return false; }
            if( this.Undefined || other.Undefined)
            {
                return false;
            }
            return 
                (
                    this.T == other.T &&
                    this.Cp == other.Cp &&
                    this.H == other.H &&
                    this.S == other.S
                );
        }

        #endregion

        public static bool operator == (ThermoRef ref1, ThermoRef ref2) { return ref1.Equals(ref2); }

        public static bool operator != (ThermoRef ref1, ThermoRef ref2) { return (!ref1.Equals(ref2)); }

        /// <summary>
        /// Compares two objects with the accuracy determined by the given tolerance
        /// </summary>
        /// <typeparam name="TTolerance">Generic tolerance type</typeparam>
        /// <param name="other">The ThermoRef object to compare with the current ThermoRef</param>
        /// <param name="tolerance">Tolerance determining the comparison accuracy</param>
        /// <returns>true if the specified ThermoRef object is identical to this instance; otherwise, false</returns>
        public bool IsSame<TTolerance>(ThermoRef other, TTolerance tolerance) where TTolerance : ITolerance<double>
        {
            return
                tolerance.AreSame(this.T, other.T) &&
                tolerance.AreSame(this.Cp, other.Cp) &&
                tolerance.AreSame(this.H, other.H) &&
                tolerance.AreSame(this.S, other.S);

        }

    }

    
}
